package nl.scalasim
package actors
package traits

import nl.scalasim.util.{Storage, Accumulator}
import scala.actors.{OutputChannel, Actor}
import nl.scalasim.controlactors.Guildmaster

/**
 * A Harvesting actor harvests goods at a specified rate. Each time a
 * harvesting actor receives a 'Process' messages, it replies with a
 * 'Stored(goods, amount, remaining)' message. 
 *
 * @author Yigal Duppen
 * @since Oct 5, 2010
 */
trait HarvestingActor extends SimulationActor {

  val goods: Goods

  val speed: Int

  val capacity: Int

  private var accumulator = Accumulator(speed)

  private val storage = new Storage(Map(goods -> capacity), capacity)

  private var producer: Option[SimulationActor] = None

  private[traits] override def stored = storage.view

  private[traits] override def maxStorageCapacity = capacity


  def advertise(g: Guildmaster) = {
    log.info(this + " advertises " + storage.view)
    g ! Advertise(this, storage.view)
  }


  def handleConnect: PartialFunction[Any, Unit] = {
    case Connect(a) => {
      producer = Some(a)
      addRelated(a)
      reply(Ack)
    }
  }
  callbacks :+= handleConnect


  def handleProcess: PartialFunction[Any, Unit] = {
    case Process => {
      producer match {
        case None => reply(Stored(goods, 0))
        case Some(p) => {
          val s = sender
          harvestAndStore(p, s)
        }
      }
    }
  }
  callbacks :+= handleProcess


  def handleRetrieve: PartialFunction[Any, Unit] = {
    case Retrieve(goods, amount) => {
      val retrieved = storage.retrieve(goods, amount)
      reply(Retrieved(goods, retrieved))
    }
  }
  callbacks :+= handleRetrieve


  private def harvestAndStore(producer: Actor, s: OutputChannel[Any]): Unit = {
    accumulator = accumulator.accumulate()
    (accumulator.overflow, storage.remainingCapacity(goods)) match {
      case (0, _) => reply(Stored(goods, 0))
      case (_, 0) => reply(Stored(goods, 0))
      case _ => {
        producer ! Process
        react {
          case Produced(goods, amount) => {
            val result = storage.store(goods, amount)
            s ! Stored(goods, result)
          }
        }
      }
    }
  }

}